package org.dreamwork.jasmine2.configure;

import org.dreamwork.config.ApplicationConfigParser;
import org.dreamwork.i18n.LocaleUtil;
import org.dreamwork.jasmine2.restful.APIDefinition;
import org.dreamwork.jasmine2.restful.RestfulAPIXmlConfigParser;
import org.dreamwork.util.StringUtil;

import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: seth.yang
 * Date: 12-10-22
 * Time: 下午2:51
 */
public class JasmineConfig {
    public static final String KEY_DEBUG = "debug";
    public static final String KEY_CLASSLOADER = "classloader";
    public static final String KEY_IO_BASE_DIR = "io.basedir";
    public static final String KEY_IO_MAX_UPLOAD_SIZE = "io.max-upload-size";
    public static final String KEY_IO_LIMIT_EXCEED_PAGE = "io.limit-exceed-page";
    public static final String KEY_I18N_RESOURCE = "i18n.resource";
    public static final String KEY_I18N_DEFAULT_LOCALE = "i18n.default-locale";
    public static final String KEY_I18N_RESOURCE_MANAGER = "i18n.resource-manager-factory";
    public static final String KEY_I18N_LOAD_ON_STARTUP = "i18n.load-on-startup";
    public static final String KEY_EDITOR_PRIVATE_FOLDER_FETCHER = "private-folder-fetcher";
    public static final String KEY_EDITOR_PRIVATE_FOLDER_SUPPORTED = "editor.private-folder-supported";

    public static final String JASMINE_CONFIG_FILE = "jasmine.config.file";
    public static final String JASMINE_I18N_HOLDER = "jasmine.i18n.resource.manager";
    public static final String SUPPORTED_LOCALES = "jasmine.i18n.supported.locales";
    public static final String DEFAULT_LOCALE = "jasmine.i18n.default.locale";

    public static boolean debug;
    public static IO io;
    public static I18n i18n;
    public static Editor editor;
    public static Map<String, APIDefinition> apiDefinitionMap;
    public static String restfulApiConfigURL, ssoConfigURL, classloader;

    public static void parse (URL configFile) throws Exception {
        ApplicationConfigParser parser = new ApplicationConfigParser (configFile);
        parser.addParserClass ("restful-api", RestfulAPIXmlConfigParser.class);

//        InputStream in = JasmineConfig.class.getResourceAsStream ("/META-INF/jasmine.config.xsd");
//        try {
//            parser.validateByXSD (in);
            parser.parse ();
//        } finally {
//            in.close ();
//        }

        if (parser.contains (KEY_CLASSLOADER)) {
            classloader = parser.getString (KEY_CLASSLOADER);
        }
        parseDebugMode (parser);
        parseIo (parser);
        parseI18n (parser);
        parseEditor (parser);
        parseRestfulAPI (parser);
    }

    private static void parseDebugMode (ApplicationConfigParser parser) {
        if (parser.contains (KEY_DEBUG))
            debug = Boolean.valueOf (parser.getValue (KEY_DEBUG).toString ());
        else
            debug = false;
    }

    private static void parseIo (ApplicationConfigParser parser) {
        if (parser.contains ("io")) {
            if (!parser.contains (KEY_IO_BASE_DIR))
                throw new IllegalArgumentException (KEY_IO_BASE_DIR + " is need");
            String basedir = (String) parser.getValue (KEY_IO_BASE_DIR);
            String size = parser.contains (KEY_IO_MAX_UPLOAD_SIZE) ?
                    parser.getString (KEY_IO_MAX_UPLOAD_SIZE) : "";
            String page = parser.getString (KEY_IO_LIMIT_EXCEED_PAGE);
            io = new IO (basedir, size, page);
        }
    }


    private static void parseI18n (ApplicationConfigParser parser) {
        if (parser.contains ("i18n")) {
            if (!parser.contains (KEY_I18N_RESOURCE))
                throw new IllegalArgumentException (KEY_I18N_RESOURCE);
            String resource = (String) parser.getValue (KEY_I18N_RESOURCE);
            if (!parser.contains (KEY_I18N_DEFAULT_LOCALE))
                throw new IllegalArgumentException (KEY_I18N_DEFAULT_LOCALE + " is need");
            Locale locale = LocaleUtil.parseLocale ((String) parser.getValue (KEY_I18N_DEFAULT_LOCALE));

            String manager = "org.dreamwork.jasmine2.i18n.WebResourceManager";
            if (parser.contains (KEY_I18N_RESOURCE_MANAGER))
                manager = (String) parser.getValue (KEY_I18N_RESOURCE_MANAGER);

            boolean load = false;
            if (parser.contains (KEY_I18N_LOAD_ON_STARTUP)) {
                String expression = (String) parser.getValue (KEY_I18N_LOAD_ON_STARTUP);
                load = Boolean.valueOf (expression);
            }

            i18n = new I18n (resource, locale, manager, load);
        }
    }

    private static void parseEditor (ApplicationConfigParser parser) {
        if (parser.contains ("editor")) {
            boolean support = false;
            if (parser.contains (KEY_EDITOR_PRIVATE_FOLDER_SUPPORTED)) {
                String value = (String) parser.getValue (KEY_EDITOR_PRIVATE_FOLDER_SUPPORTED);
                support = Boolean.valueOf (value);
            }

            if (!parser.contains (KEY_EDITOR_PRIVATE_FOLDER_FETCHER))
                throw new IllegalArgumentException (KEY_EDITOR_PRIVATE_FOLDER_FETCHER + " is need");
            String value = (String) parser.getValue (KEY_EDITOR_PRIVATE_FOLDER_FETCHER);

            editor = new Editor (support, value);
        }
    }

    @SuppressWarnings ("unchecked")
    private static void parseRestfulAPI (ApplicationConfigParser parser) {
        if (parser.contains ("restful-api")) {
            apiDefinitionMap = (Map<String, APIDefinition>) parser.getValue ("restful-api");
        } else if (parser.contains ("restful-api-conf.ref")) {
            restfulApiConfigURL = parser.getString ("restful-api-conf.ref");
        }
    }

    public static class IO {
        public final String baseDir, limitExceedPage;
        public final int maxUploadSize;

        private IO (String baseDir, String size, String limitExceedPage) {
            this.baseDir = baseDir;

            if (StringUtil.isEmpty (size)) {
                maxUploadSize = -1;
            } else {
                char[] buff = size.toCharArray ();
                char unit = buff [buff.length - 1];
                if (unit == 'b' || unit == 'B' || (unit <= '9' && unit >= '0')) {
                    maxUploadSize = Integer.parseInt (size);
                } else if (unit == 'k' || unit == 'K') {
                    maxUploadSize = Integer.parseInt (new String (buff, 0, buff.length - 1)) * 1024;
                } else if (unit == 'm' || unit == 'M') {
                    maxUploadSize = Integer.parseInt (new String (buff, 0, buff.length - 1)) * 1024 * 1024;
                } else if (unit == 'g' || unit == 'G') {
                    maxUploadSize = Integer.parseInt (new String (buff, 0, buff.length - 1)) * 1024 * 1024 * 1024;
                } else {
                    throw new IllegalArgumentException ("Invalid max upload size");
                }
            }

            this.limitExceedPage = limitExceedPage;
        }
    }

    public static class I18n {
        public final String resource;
        public final Locale defaultLocale;
        public final boolean loadOnStartup;
        public final String resourceManager;

        private I18n (String resource, Locale defaultLocale, String resourceManager, boolean loadOnStartup) {
            this.resource = resource;
            this.defaultLocale = defaultLocale;
            this.loadOnStartup = loadOnStartup;
            this.resourceManager = resourceManager;
        }
    }

    public static class Editor {
        public final ReadonlyMap<String, String> mappings = new ReadonlyMap<String, String> ();
        public final boolean privateFolderSupported;
        public final String privateFolderFetcher;

        private Editor (boolean privateFolderSupported, String privateFolderFetcher) {
            this.privateFolderSupported = privateFolderSupported;
            this.privateFolderFetcher = privateFolderFetcher;
        }
    }

    private static class ReadonlyMap<K, V> extends HashMap<K, V> {
        @Override
        public V put (K key, V value) {
            throw new UnsupportedOperationException ("ReadonlyMap.put");
        }

        @Override
        public void putAll (Map<? extends K, ? extends V> m) {
            throw new UnsupportedOperationException ("ReadonlyMap.putAll");
        }

        @Override
        public V remove (Object key) {
            throw new UnsupportedOperationException ("ReadonlyMap.remove");
        }

        private void add (K key, V value) {
            super.put (key, value);
        }
    }
}